package api_g

/*******************************************************************************
Copyright:cloud
Author:cloudapex@126.com
Version:1.0
Date:2021-06-15
Description: 把 .proto 文件 生成 htp所需要的api文件组
*******************************************************************************/
import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func GrpcToApiService(params []string) error {
	if len(params) != 1 {
		return fmt.Errorf("No file path specified")
	}

	// 待处理的文件
	filepaths := []string{}
	if filepath.Ext(params[0]) == ".proto" { // 所选文件必须是 .proto 协议源文件
		filepaths = append(filepaths, params[0])
	} else { // 按目录处理
		filelist, err := dirWalk(params[0], ".proto")
		if err != nil {
			return err
		}
		filepaths = append(filepaths, filelist...)
	}

	// 读取包名
	name, err := readGoPackageName()
	if err != nil {
		return fmt.Errorf("readGoPackageName err:%v", err)
	}
	if name == "" {
		return fmt.Errorf("not found package name in file go.mod")
	}
	g_GoPackageName = name

	for _, fp := range filepaths {
		// 解析 proto 文件
		proto, err := handleProtoFile(fp)
		if err != nil {
			return fmt.Errorf("handleProtoFile err:%v", err)
		}

		// 修改 api 包
		dir := filepath.Dir(fp)
		// 目录往前去掉一个,找到父目录
		if err := proto.TransApiFile(filepath.Dir(dir)); err != nil {
			return fmt.Errorf("TransApiFile err:%v", err)
		}
	}
	return nil
	// // 把目标包目录剪切到指定目录
	// if err = copyFile(DEF_OUT_PACKAGE, params[1]); err != nil {
	// 	return err
	// }

	// // 移除源目录
	// return os.RemoveAll(DEF_OUT_PACKAGE)
}
func readGoPackageName() (string, error) {
	f, err := os.Open("./" + "go.mod")
	if err != nil {
		return "", err
	}

	for buf := bufio.NewReader(f); ; {
		line, err := buf.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				return "", nil
			}
			return "", err
		}
		// 去除空白
		line_ := strings.TrimSpace(line)
		if strings.HasPrefix(line_, "module ") {
			return strings.Fields(line_)[1], nil
		}
	}
}
func handleProtoFile(filePath string) (*ProtoInfo, error) {
	f, err := os.Open("./" + filePath)
	if err != nil {
		return nil, err
	}
	var proto *ProtoInfo
	var curServer, lastLine string
	var curMessage = ""
	for buf := bufio.NewReader(f); ; {
		line, err := buf.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				return proto, nil
			}
			return nil, err
		}

		// 去除空白
		line_ := strings.TrimSpace(line)

		if strings.HasPrefix(line_, "}") {
			curMessage = ""
			continue
		}

		// 提取 service name
		if strings.HasPrefix(line_, "package ") {
			packag := strings.FieldsFunc(line_, func(r rune) bool {
				if r == ' ' || r == ';' {
					return true
				}
				return false
			})[1]
			fields := strings.Split(packag, ".")
			if len(fields) < 2 {
				return nil, fmt.Errorf("not found service name for %q", line)
			}
			proto = &ProtoInfo{
				FileName: filepath.Base(filePath),
				Project:  fields[len(fields)-2],
				Service:  fields[len(fields)-1],
				Messages: map[string][]string{ /*"comm.Request": nil, "comm.Response": nil*/ },
				Serversm: map[string]*ServerInfo{},
			}
			continue
		}

		// 提取 package name
		if strings.HasPrefix(line_, "option go_package") {
			if proto == nil {
				return nil, fmt.Errorf("miss service name for %q", line)
			}
			proto.Package = strings.Split(line_, "\"")[1]
		}

		// 提取 message
		if strings.HasPrefix(line_, "message") {
			if proto == nil {
				return nil, fmt.Errorf("miss service name for %q", line)
			}
			message := strings.FieldsFunc(line_, func(r rune) bool {
				if r == ' ' || r == '\t' || r == '{' {
					return true
				}
				return false
			})[1]
			proto.Messages[message] = []string{}
			curMessage = message
		}

		if curMessage != "" {
			if !strings.HasPrefix(line_, "//") && strings.Contains(line_, "=") && strings.Contains(line_, ";") {
				note := ""
				fields := strings.Split(line_, "//")
				if len(fields) > 1 {
					note = strings.TrimSpace(fields[len(fields)-1])
				}
				proto.Messages[curMessage] = append(proto.Messages[curMessage], fmt.Sprintf("%q", note))
			}
		}
		// 提取 server name
		if strings.HasPrefix(line_, "service") {
			if proto == nil {
				return nil, fmt.Errorf("miss service name for %q", line)
			}
			server := strings.FieldsFunc(line_, func(r rune) bool {
				if r == ' ' || r == '\t' || r == '{' {
					return true
				}
				return false
			})[1]
			curServer = server
			serverInfo := &ServerInfo{Name: server, Rpcsm: map[string]*RpcInfo{}}
			proto.Servers = append(proto.Servers, serverInfo)
			proto.Serversm[server] = serverInfo
		}
		// 提取 rpc function (不支持流式)
		if strings.HasPrefix(line_, "rpc ") && !strings.Contains(line_, "stream ") {
			if proto == nil {
				return nil, fmt.Errorf("miss service name for %q", line)
			}
			fields := strings.FieldsFunc(line_, func(r rune) bool {
				if r == '(' || r == ')' {
					return true
				}
				return false
			})
			title := ""
			if strings.HasPrefix(lastLine, "//") {
				title = strings.TrimSpace(lastLine[2:])
			}

			fields_fun := strings.FieldsFunc(line_, func(r rune) bool {
				if r == ' ' || r == '(' {
					return true
				}
				return false
			})
			rpc := &RpcInfo{title, fields_fun[1], fields[1], fields[len(fields)-2]}
			// if _, ok := proto.Messages[rpc.InReq]; !ok {
			// 	return nil, fmt.Errorf("miss message %s for %q", rpc.InReq, line)
			// }
			// if _, ok := proto.Messages[rpc.OutResp]; !ok {
			// 	return nil, fmt.Errorf("miss message %s for %q", rpc.OutResp, line)
			// }
			proto.Serversm[curServer].Rpcs = append(proto.Serversm[curServer].Rpcs, rpc)
			proto.Serversm[curServer].Rpcsm[rpc.Func] = rpc
		}
		lastLine = line_
	}
}
func GoFmtFile(wait bool, arg ...string) (string, error) {
	c := exec.Command("gofmt", arg...)
	if !wait {
		if err := c.Start(); err != nil {
			return "", err
		}
		return "", nil
	}
	out := bytes.NewBuffer(nil)
	c.Stderr = out
	if err := c.Run(); err != nil {
		return out.String(), err
	}
	return out.String(), nil
}

// ---------------- internal

// 遍历目录(匹配文件后缀并返回文件Path[会进入子目录])
func dirWalk(dirPth, suffix string) (files []string, err error) {
	files = make([]string, 0, 30)
	suffix = strings.ToUpper(suffix) //忽略后缀匹配的大小写
	err = filepath.Walk(dirPth, func(filename string, fi os.FileInfo, er error) error {
		if er != nil {
			return er
		}
		if fi.IsDir() {
			return nil
			//return filepath.SkipDir 如果想要忽略这个目录的内容则返回filepath.SkipDir
		}
		if suffix == "*" || suffix == "" {
			files = append(files, filename)
		} else if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) {
			files = append(files, filename)
		}
		return nil
	})
	return files, err
}

// 拷贝目录
func copyFile(src, des string) error {
	f, err := os.Stat(src)
	if err != nil {
		return err
	}
	if f.IsDir() { // dir to dir
		list, err := ioutil.ReadDir(src)
		if err != nil {
			return err
		}
		// 递归
		for _, item := range list {
			if err := copyFile(filepath.Join(src, item.Name()), filepath.Join(des, item.Name())); err != nil {
				return err
			}
		}
	} else { // file to file

		// ready des dir
		path := filepath.Dir(des)
		if os.MkdirAll(path, 0777); err != nil {
			return err
		}

		// from stream
		from, err := os.Open(src)
		if err != nil {
			return err
		}
		defer from.Close()

		// to stream
		to, err := os.Create(des)
		if err != nil {
			return err
		}
		defer to.Close()

		// copy stream
		_, err = io.Copy(to, from)
	}
	return err
}
